package com.loklok.code.confuse.util;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.ui.Messages;
import com.loklok.code.confuse.dto.GenerateCodeDto;
import com.loklok.code.confuse.model.CodePathConfig;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.log.NullLogChute;
import org.jetbrains.annotations.NotNull;

import java.io.StringWriter;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Classname VelocityUtils
 * @Description
 * @Date 2023/5/8 17:00
 * @Created by wangchangjiu
 */
public class VelocityUtils {



    public static String getMethodText(String templatePath, String pckName, Set<String> methods) {
        VelocityContext velocityContext = new VelocityContext();
        velocityContext.put("classPrefix", CommonUtil.pckToClassPrefix(pckName));
        velocityContext.put("methods", methods);

        String generateCode = VelocityUtils.generateCode(velocityContext, templatePath, "Methods");
        return generateCode;
    }



    public static GenerateCodeDto generateDtoVoCode(CodePathConfig config, String voPath, String pckName, boolean isChooseVo) {

        VelocityContext velocityContext = new VelocityContext();

        String importPackagePath;
        String pathToPackage;
        if(isChooseVo){
            importPackagePath = config.getRequestPath();
            pathToPackage = config.getVoPath();
        } else {
            importPackagePath = config.getVoPath();
            pathToPackage = config.getRequestPath();
        }

        putCommonData(pathToPackage, config.getCodeAuthor(), pckName, velocityContext);

        fillSynonym(config, voPath, velocityContext);

        String finalImportPackagePath = importPackagePath;
        velocityContext.put("importPackage", new HashSet<String>() {{
            add(CommonUtil.pathToImportPackage(CommonUtil.pathJoint(finalImportPackagePath, pckName)));
        }});


        // E://A/d/EE.java.vm
        Path path = Paths.get(voPath);

        // 获取目录路径
        Path directoryPath = path.getParent();

        // 获取文件名
        String className = path.getFileName().toString().replace(".vm", "");
        String generateCode = VelocityUtils.generateCode(velocityContext, directoryPath.toString(), className);
        return GenerateCodeDto.buildCreateFile(CommonUtil.pathToPackage(CommonUtil.pathJoint(pathToPackage, pckName)), CommonUtil.pckToClassPrefix(pckName) + className, generateCode, JavaFileType.INSTANCE);
    }

    public static GenerateCodeDto generateControllerCode(CodePathConfig config, String name, String templatePath, Set<String> methodNames) {
        VelocityContext velocityContext = new VelocityContext();
        putCommonData(config.getControllerPath(), config.getCodeAuthor(), name, velocityContext);
        velocityContext.put("path", name);
        velocityContext.put("importPackage", new HashSet<String>() {{
            add(CommonUtil.pathToImportPackage(CommonUtil.pathJoint(config.getRequestPath(), name)));
            add(CommonUtil.pathToImportPackage(CommonUtil.pathJoint(config.getVoPath() , name)));
        }});
        if(CollectionUtil.isNotEmpty(methodNames)){
            velocityContext.put("methods", methodNames);
        }

        // E://A/d/EE.java.vm
        Path path = Paths.get(templatePath);

        // 获取目录路径
        Path directoryPath = path.getParent();

        // 获取文件名
        String className = path.getFileName().toString().replace(".vm", "");
        String generateCode = VelocityUtils.generateCode(velocityContext, directoryPath.toString(), className);
        return GenerateCodeDto.buildCreateFile(CommonUtil.pathToPackage(CommonUtil.pathJoint(config.getControllerPath() , name)), CommonUtil.pckToClassPrefix(name) + className, generateCode, JavaFileType.INSTANCE);
    }

    private static void fillSynonym(CodePathConfig config, String templatePath, VelocityContext velocityContext) {
        Set<String> paramNames = parseParams(templatePath);

        if(CollectionUtil.isEmpty(paramNames)){
            return;
        }

        Map<String, Set<String>> synonym = new HashMap<>();
        if(paramNames.size() > config.getBatch()){
             Lists.partition(new ArrayList<>(paramNames), config.getBatch())
                    .stream()
                    .map(HashSet::new)
                    .collect(Collectors.toList()).forEach(partitionParamNames -> synonym.putAll(doDeepSeekGetSynonym(config, partitionParamNames)));
        } else {
            synonym.putAll(doDeepSeekGetSynonym(config, paramNames));
        }


        synonym.forEach((key, val) -> {
            String randomElement = val.stream()
                    .skip(new Random().nextInt(val.size()))
                    .findFirst().get();
            randomElement = randomElement.replaceAll(" ", "");
            velocityContext.put(key, randomElement);
        });
    }

    private static Map<String, Set<String>> doDeepSeekGetSynonym(CodePathConfig config, Set<String> paramNames) {
        String prompt = String.format(config.getDeepSeekPrompt(), String.join(",", paramNames));
        Map<String, Set<String>> synonym = new HashMap<>();
        try {
            synonym = SyncOpenAIUtil.getSynonym(config.getDeepSeekApiKey(), prompt);

        } catch (Exception e) {
            System.out.println(e);
            ApplicationManager.getApplication().invokeLater(() ->  Messages.showMessageDialog("deep seek 获取近似词失败,请检查账号可用性或者每次获取近义词的个数过多", "error massage:", Messages.getErrorIcon()));
            throw new RuntimeException(e);
        }
        return synonym;
    }

    @NotNull
    private static Set<String> parseParams(String templatePath) {
        String readString = LocalFileUtils.fileReadString(templatePath);
        Set<String> paramNames = CommonUtil.regularExtractParams(readString);
        paramNames.removeAll(Arrays.asList("packageName","importPackage","classPrefix"));
        return paramNames;
    }



    private static void putCommonData(String configPath, String codeAuthor, String name, VelocityContext velocityContext) {
        velocityContext.put("packageName", CommonUtil.pathToDeclarePackage(configPath) + "." + name);
        velocityContext.put("classPrefix", CommonUtil.pckToClassPrefix(name));
        velocityContext.put("author", codeAuthor);
    }


    @NotNull
    public static String generateCode(VelocityContext velocityContext, String templatePath, String fileName) {
        final ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(VelocityUtils.class.getClassLoader());

        //实例化
        VelocityEngine velocityEngine = new VelocityEngine();

        //关闭日志
        velocityEngine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new NullLogChute());
       // velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
       // velocityEngine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
        // 设置资源加载器为 FileResourceLoader
        velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "file");

        // 显式设置输入和输出编码为 UTF-8
        velocityEngine.setProperty("input.encoding", "UTF-8");
        velocityEngine.setProperty("output.encoding", "UTF-8");

        // 设置资源加载器的路径
        //velocityEngine.setProperty(FileResourceLoader.FILE_RESOURCE_LOADER_PATH, "/path/to/templates");
        velocityEngine.setProperty("file.resource.loader.path", templatePath);
        velocityEngine.init();

        StringWriter writer = new StringWriter();
        //生成代码
        velocityEngine.getTemplate(fileName + ".vm", "UTF-8").merge(velocityContext, writer);

        String code = writer.toString();

        // 清理代码中的潜在问题字符
        code = cleanGeneratedCode(code);

        Thread.currentThread().setContextClassLoader(oldContextClassLoader);
        return code.replaceAll("\r\n", "\n");
    }


    /**
     * 清理生成的代码，移除可能导致问题的字符
     */
    private static String cleanGeneratedCode(String code) {
        if (code == null || code.isEmpty()) {
            return code;
        }

        // 移除可能的 BOM 标记
        if (code.startsWith("\uFEFF")) {
            code = code.substring(1);
        }

        // 移除控制字符（保留换行符、制表符等常用空白字符）
        code = code.replaceAll("[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]", "");

        return code;
    }
}
